(function(){

    function Calendar(){
		Document.call(this);

		//***************Calendar constants*****************
		Calendar.patern_format = new RegExp(/(([myd]{2}|y{4})[\W_]?){3}/i);

		/**
	     * to avoid extro quantity of format elements together('mm' not 'mdm')
	     * general quantity is checking in this.checkiong_date_format_conditions()
	     */
        Calendar.patern_format_uncorrect_quantity = new RegExp(/m{3,}|d{3,}|y{5,}|([\W_dm]{1}y{3}(?!y))/i);
		Calendar.block_name = 'calendar';
		Calendar.days_in_week = 7;
		Calendar.number_of_years = 199;
		Calendar.start_year = 1900;

		this.handler = new Handler();

        this.month_names = Array(['Январь',
                                  'Февраль',
                                  'Март',
                                  'Апрель',
                                  'Май',
                                  'Июнь',
                                  'Июль',
                                  'Август',
                                  'Сентябрь',
                                  'Октябрь',
                                  'Ноябрь',
                                  'Декабрь'
                                 ],
                                 ['January',
                                  'February',
                                  'March',
                                  'April',
                                  'May',
                                  'June',
                                  'July',
                                  'August',
                                  'September',
                                  'October',
                                  'November',
                                  'December'
                                  ]
                                  );

        this.language = new Array('rus',
                                  'eng'
                                  );
        this.table_header_html = new Array(['Пн',
                                           	'Вт',
                                           	'Ср',
                                           	'Чт',
                                           	'Пт',
                                           	'Сб',
                                           	'Вс'
                                           	],
                                           ['Mn',
											'Tu',
                                           	'Wd',
                                           	'Th',
                                           	'Fr',
                                           	'St',
                                           	'Su'
											]
                                           );

        this.today = {
            			year :new Date().getFullYear(),
            			month:new Date().getMonth(),
            			date :new Date().getDate()
        			 };
        this.block = this.get_element_by_id(Calendar.block_name);
        this.selected_options = this.today;
        this.clicked_cell_info = this.today;
        this.date_format_info = 'set format';
        this.language_active = '';
        this.set_format = this.set_format || ' dd-mm-yy';

        this.ZendDate = this.get_element_by_id('date');

        //for diverse variant of elements quantity(e.g. m||mm etc.)
        this.adjusted_format_element = {}


    }

	/**
     * enharitance from Document
     */
	Calendar.prototype = new Document();
	//******************events methods****************

    /**
     * changing of the context of the EventHandler
     */
    Function.prototype.bind = function(object) {
        var method = this;
        return function(){
            return method.apply(object, arguments);
        }
    }

    /**
     * -----------getting of the month length-------------
     */
    Calendar.prototype.get_month_length = function(year,month){
    	return new Date(year, ++month, 0).getDate();
    }

	/**
     * -----------register_selects_event_handlers-------------
     */
    Calendar.prototype.register_selects_event_handlers = function(lang){

        var select_m = this.get_element_by_id('sel_month');
        var select_y = this.get_element_by_id('sel_year');

        this.handler.add(select_m,'change',function(e){
            var e = e || window.event;
            var selected_option = e.currentTarget || e.srcElement;
            var option_value =  selected_option.options[selected_option.selectedIndex].value;
            this.selected_options = {
                year :this.selected_options.year,
                month:parseInt(option_value),
                date :this.today.date
            }
            this.remove_children(this.block);
            this.make_calendar();
        }.bind(this));

        this.handler.add(select_y,'change',function(e){
            var e = e || window.event;
            var selected_option = e.currentTarget || e.srcElement;
            var option_value =  selected_option.options[selected_option.selectedIndex].text;
            this.selected_options = {
                year :parseInt(option_value),
                month:this.selected_options.month,
                date :this.today.date
            }
            this.remove_children(this.block);
            this.make_calendar();
        }.bind(this));

        /**
		 * -----------register eventHandlers for next and prev settters of month-------------
    	 */
        var span_prev = this.get_element_defenite_descendants(this.block,'span')[0];
        var span_next = this.get_element_defenite_descendants(this.block,'span')[1];

        this.handler.add(span_prev,'click',function(){
            if(this.selected_options.month <= 11 && this.selected_options.month > 0){
                this.selected_options.month -= 1;
            }else if(this.selected_options.month <= 0){
                if(this.selected_options.year === Calendar.start_year){
					this.selected_options.year = 2099;
				}else{
					this.selected_options.year -= 1;
				}
                this.selected_options.month = 11;
            }
            this.remove_children(this.block);
            this.make_calendar();
        }.bind(this));

        this.handler.add(span_next,'click',function(e){
            if(this.selected_options.month >= 0 && this.selected_options.month < 11){
                this.selected_options.month += 1;
            }else if(this.selected_options.month = 11){
                this.selected_options.year += 1;
                this.selected_options.month = 0;
            }
            this.remove_children(this.block);
            this.make_calendar();
        }.bind(this));

       /**
		 * -----------register eventHandlers for setting of the date format-------------
    	 */
        var set_format_input = this.get_elements_by_name('format','first');

		this.handler.add(set_format_input,'change',function(e){
            var e = e || window.event;
            var input_format = e.currentTarget || e.srcElement;
            input_format.value += '';//else wont take empty value

            //correct format(return position of the first letter(must be 0))
            if(this.checkiong_date_format_conditions() === true){
                this.set_format = input_format.value;
                this.date_format_info = 'click any date';
                this.remove_children(this.block);
                this.make_calendar();
            }else{
                this.set_format = 'uncorrect format';
                this.date_format_info = 'invalid format';
                this.remove_children(this.block);
                this.make_calendar();
            }

        }.bind(this));

	   /**
		 * -----------register eventHandlers for focusing on format input-------------
    	 */
        var set_format_input = this.get_elements_by_name('format','first');

		this.handler.add(set_format_input,'focus',function(e){
            var e = e || window.event;
            var input = e.currentTarget || e.srcElement;
            if(this.set_format === 'dd-mm-yy' ||this.set_format === 'uncorrect format'){
                input.value = '';
            }
        }.bind(this));

	   /**
		 * -----------register eventHandlers for click on the table cells-------------
    	 */
        var td = this.get_element_children(this.block,'td');
        if(this.checkiong_date_format_conditions() === true){
            for(var i = 0; i < td.length; i++){

                //if uncorrected format is set cancel registration of handlers
                this.handler.add(td[i],'click',function(e){
                    var e = e || window.event;
                    var cell_click = e.currentTarget || e.srcElement;
                    var cell_content =  cell_click.innerHTML;
                    var format = this.set_format;
                    cell_content = parseInt(cell_content);
                    this.clicked_cell_info = this.selected_options;
                    this.clicked_cell_info.date = (cell_content)?cell_content:this.clicked_cell_info.date;
                    this.get_adjusted_elements_quantity();
                    format = format.replace(/dd/i,this.adjusted_format_element.date);
                    format = format.replace(/mm/i,this.adjusted_format_element.month);
                    format = format.replace(/(yyyy|yy)/i,this.adjusted_format_element.year);
                    this.date_format_info = format;
                    this.remove_children(this.block);
                    this.make_calendar();
                }.bind(this));
            }
        }

        /**
		 * -----------eventHandler when hovering above table cells-------------
    	 */
        var td = this.get_element_children(this.block,'td');
        for(var i = 0; i < td.length; i++){
            this.handler.add(td[i],'mouseover',function(e){
                var e = e || window.event;
                var cell_target =  e.currentTarget || e.srcElement;
                var td_handler = this.get_element_children(this.block,'td');
                if(cell_target['className'] !== 'td_hover'){
                    cell_target['className'] = 'calendar_table_td_hover';
                }
            }.bind(this));
        }
        var td = this.get_element_children(this.block,'td');
        for(var i = 0; i < td.length; i++){
            this.handler.add(td[i],'mouseout',function(e){
                var td_handler = this.get_element_children(this.block,'td');
                for(var i = 0; i < td_handler.length; i++){
                    if(td_handler[i]['className'] != 'td_hover'){
                        td_handler[i]['className'] = null;
                    }
                }
            }.bind(this));
        }

        /**
		 * -----------eventHandler for changing a language-------------
    	 */
        var div = this.get_element_children(this.block,'div','first');
        var span_lang_array = this.get_element_children(div,'span');
        for(var i = 0; i < span_lang_array.length; i++){
            this.handler.add(span_lang_array[i],'click',function(e){
                var e = e || window.event;
                var span_target =  e.currentTarget || e.srcElement;
                this.language_active = span_target.innerHTML;
                this.remove_children(this.block);
                this.make_calendar();
            }.bind(this));
        }
    }
	//--------------------end of registering eventsHandlers----------------

	Calendar.prototype.checkiong_date_format_conditions = function(){
        var input_format = this.get_element_by_id('format');

        if( input_format.value.search(Calendar.patern_format) >= 0                       &&
            input_format.value.search(Calendar.patern_format_uncorrect_quantity) === -1  &&
            input_format.value.match(/m/gi).length === 2					             &&
            input_format.value.match(/d/gi).length === 2					             &&
            input_format.value.match(/y/gi).length <= 4
            )
		{
			if(input_format.value.match(/y/gi).length === 4 &&
			   input_format.value.search(/[\W_md]+y{2}[\W_md]+/i) >= 0
			  )
			{
				return false;
			}
            return true;
        }
		else
		{
            return false;
        }
    }

    /**
     * method of setting of the date elements quantity to adjust set format
     * !!!it will only work if format has 'mm','dd','yy'||'yyyy' in any order and diffrent separators between
	 * elements quantity limitation is setting in this.checkiong_date_format_conditions()
     */
    Calendar.prototype.get_adjusted_elements_quantity = function(){
        var date = parseInt(this.clicked_cell_info.date);
        var month = parseInt(this.clicked_cell_info.month);
        var year = parseInt(this.clicked_cell_info.year);

        //adding of '0' for correct format 'mm'
        if(month < 9){
            this.adjusted_format_element.month = '0' + (month + 1);
        }else{
            this.adjusted_format_element.month  = month + 1;
        }
        if(date < 10){
            this.adjusted_format_element.date = '0' + date;
        }else{
            this.adjusted_format_element.date = date;
        }
        if((this.set_format.search(/y{2}/i) >= 0) && (this.set_format.search(/y{4}/i) === -1)){
            this.adjusted_format_element.year = 'x' + year;
            this.adjusted_format_element.year = this.adjusted_format_element.year.replace(/(x\d{2})/i,'');
        }else if(this.set_format.search(/y{4}/i) >= 0){
            this.adjusted_format_element.year = year;
        }
    }

	//--------------methods of creation of the table-----------
    Calendar.prototype.make_table = function(lang){
        var block = this.block;
        this.create_and_append_element('table',{
            							name:'cal_tab',
            							id:'cal_tab'
        								},block);
        var 	table = this.get_element_defenite_descendants(this.block,'table')[0];
        table =  this.create_and_append_element('tbody',table);
        var tr_header = this.create_and_append_element('tr',table);
		tr_header['className'] = 'tr_header';
        for(var j = 0; j < Calendar.days_in_week; j++){
            if(lang === this.language[0]){
				this.create_and_append_element('th',tr_header,this.table_header_html[0][j]);
			}else if(lang === this.language[1]){
				this.create_and_append_element('th',tr_header,this.table_header_html[1][j]);
			}
		}

        var year = this.selected_options.year;
        var month = this.selected_options.month;
        var month_length = this.get_month_length(year,month);//getting of the month length
        var day_of_month_start = (this.get_month_start(year,month) == 0)?Calendar.days_in_week:this.get_month_start(year,month);
        this.create_and_append_element('tr',table);
        var tr = this.get_element_children(table,'tr','last');//****
	    //i from 0  for correct input of rows(tr 0%7 == 0)
        for(var i = 0; i < (month_length + day_of_month_start-1); i++){

			//-------+2 course '+1':for(i=0;'+1':day_of_month_start -start-------
			//-------and I need  empty cells if they are on the first line-------
            var cell_content = (i + 2) - day_of_month_start;
            if(i <= 6){//-----for first week day_of_month_start-2
                if(i <= (day_of_month_start-2)){
                    this.create_and_append_element('td',tr,' ');
                    continue;
                }
                this.create_and_append_element('td',tr,cell_content);
                continue;
            }
            if(i%Calendar.days_in_week === 0){
                this.create_and_append_element('tr',table);
                tr = this.get_element_children(table,'tr','last');
                this.create_and_append_element('td',tr,cell_content);
            }else{
                this.create_and_append_element('td',tr,cell_content);
            }
        }
    }

    //--------------main method of the calendar creation (include main other ones)-----------
	Calendar.prototype.make_calendar = function(){
        var lang = this.language_active || this.language[0];
        this.make_language_block();
        this.make_prev_month_setter();
        this.make_select_months(lang);
        this.make_select_years();
        this.make_next_month_setter();
        this.make_table(lang);
        this.make_date_info_input();
        this.make_date_info_input_month(lang);
        this.make_date_format_input();
        this.find_and_mark_currenet_date();
        this.register_selects_event_handlers(lang);


    }

    Calendar.prototype.get_month_start = function(year,month){
        var active_date_info = new Date(year,month,1);
        return active_date_info.getDay();
    }

    Calendar.prototype.find_and_mark_currenet_date = function(){
        var today = new Date();
        var current_date = today.getDate();
        var calendar_td = this.get_element_children(this.block,'td');
        for(var i = 0; i < calendar_td.length; i++){
            var current_td_value = calendar_td[i].innerHTML;
            if(current_date === parseInt(current_td_value)){
                calendar_td[i]['className'] = 'td_hover';//for ie
                return calendar_td[i];
            }
        }
    }

    //**********************methods for  making DOM elements*******************

	Calendar.prototype.make_next_month_setter = function(){
        this.create_and_append_element('span',{
           						 		name:'next_setter'
        								},this.block,"&gt;");
    }

    Calendar.prototype.make_prev_month_setter = function(){
        this.create_and_append_element('span',
									   {name:'prev_setter'},
									   this.block,"&lt;");
    }

    Calendar.prototype.make_date_info_input_month = function(lang){
        var month_names = (lang === 'eng')?this.month_names[1]:this.month_names[0];
        for(var i = 0 ; i < month_names.length; i++){
            if(this.clicked_cell_info.month === i){
                var date_info_input_month = month_names[i];
                break;
            }
        }
        this.create_and_append_element('abbr',
            							{name:'date_info_input_month'},
        								this.block,date_info_input_month);
    }

    Calendar.prototype.make_date_info_input = function(){
        this.create_and_append_element('abbr',
           								{name:'date_info_input',
           								 id  :'date_info_input'
           								},
        								this.block,this.date_format_info);
    }

    Calendar.prototype.make_date_format_input = function(){
        var input = this.set_element_inner_html('input');
        input.type = 'text';
        input.value = input.value || this.set_format;
        this.create_and_append_element(input,
           							   {name:'format',
            						   id:'format',
									   title:'mm-dd-yy, mM:dD:yyYY, dd#mm#yyyy etc.'},
       								   this.block);

    }

    Calendar.prototype.make_select_months = function(lang){
        var block = this.get_element_by_id('calendar');
        var month_array = (lang === this.language[1])?this.month_names[1]:this.month_names[0];
        var select = this.set_element_inner_html('select');
        for(var i = 0; i < month_array.length; i++){
            select.options[i] = new Option(month_array[i],i);
            if(i === this.selected_options.month){
                select.options[i].selected = true;
            }
        }
        this.create_and_append_element(select,
            						   {name:'sel_month',
           							   id:'sel_month'},
       								   block);
    }

    Calendar.prototype.make_select_years = function(){
        var block = this.get_element_by_id('calendar');
        var select = this.set_element_inner_html('select');
        for(var i = 0; i <= Calendar.number_of_years; i++){
            select.options[i] = new Option(i + Calendar.start_year,i); //+1900 else does not work:)
            if(i + Calendar.start_year === this.selected_options.year){
                select.options[i].selected = true;
            }
        }
        this.create_and_append_element( select,
            							{name:'sel_year',
            							id:'sel_year'},
       									block);
    }

	//--------------make language_block ------------------
    Calendar.prototype.make_language_block = function(){
        var innerHTML = '<span class="rus">rus</span>|<span class="eng">eng</span>';
        this.create_and_append_element('div',
										this.block,
										innerHTML);
        var lang_block = this.get_element_children(this.block,
													'div',
													'first');
        lang_block['className'] = 'lang_block';

    }

    new Calendar().make_calendar()

})();